home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / SystemEventQueueUtilities.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  23.6 KB  |  881 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)SystemEventQueueUtilities.java    1.19 98/09/08
  3.  *
  4.  * Copyright 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package javax.swing;
  15.  
  16. import java.awt.*;
  17. import java.awt.event.*;
  18. import java.awt.image.*;
  19.  
  20. import java.util.Hashtable;
  21. import java.util.Enumeration;
  22. import java.util.Vector;
  23.  
  24. import java.lang.reflect.InvocationTargetException;
  25.  
  26.  
  27. /**
  28.  * Swing internal utilities for dealing with the AWT system event
  29.  * queue.  Four methods are exported, see the individual method javadoc
  30.  * for more information: addRunnableCanvas(), removeRunnableCanvas(),
  31.  * postRunnable(), queueComponentWorkRequest().
  32.  *
  33.  * @see RepaintManager
  34.  * @see JRootPane
  35.  */
  36.  
  37. class SystemEventQueueUtilities
  38. {
  39.     private static Hashtable rootTable = new Hashtable(4);
  40.  
  41.  
  42.     /**
  43.      * SystemEventQueue class.  This private class just exists to 
  44.      * encapsulate the details of getting at the System Event queue 
  45.      * in JDK1.2 and JDK1.1.  The rest of the SystemEventQueueUtilities 
  46.      * class just uses SystemEventQueue.get() to access the event queue.
  47.      */
  48.  
  49.     
  50.  
  51.     private static class SystemEventQueue 
  52.     {
  53.         private static Toolkit tk = null;
  54.  
  55.     // Return the AWT system event queue.  JDK1.2 applications 
  56.     // and PlugIn enabled browsers allow direct access to the 
  57.     // applet specific system event queue.
  58.     //
  59.     static EventQueue get() {
  60.             if (tk == null) {
  61.             tk = Toolkit.getDefaultToolkit();
  62.         }
  63.         return tk.getSystemEventQueue();
  64.     }
  65.  
  66.     static EventQueue get(JRootPane rootPane) {
  67.         return rootPane.getToolkit().getSystemEventQueue();
  68.     }
  69.     }
  70.  
  71.     
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.     /**
  160.      * A Runnable with a component.  If we need to post this
  161.      * runnable to the AWT system event queue, we'll find it's
  162.      * JRootPane ancestor and use that as the key to the table
  163.      * of RunnableCanvas's.
  164.      * <p>
  165.      * Extended by RepaintManager.WorkRequest()
  166.      *
  167.      * @see RunnableCanvas
  168.      */
  169.     private static class ComponentWorkRequest implements Runnable
  170.     {
  171.     boolean isPending;
  172.     Component component;
  173.  
  174.     ComponentWorkRequest(Component c) {
  175.         component = c;
  176.     }
  177.  
  178.     public void run() {
  179.         RepaintManager rm;
  180.         synchronized (this) {
  181.         rm = RepaintManager.currentManager(component);
  182.         isPending = false;
  183.         }
  184.         rm.validateInvalidComponents();
  185.         rm.paintDirtyRegions();
  186.     }
  187.     }
  188.  
  189.  
  190.     /**
  191.      * This method is used by RepaintManager to queue a ComponentWorkRequest
  192.      * with invokeLater().  It assumes that the root argument is either
  193.      * and Applet or a Window, see SwingUtilities.getRoot().
  194.      */
  195.     static void queueComponentWorkRequest(Component root)
  196.     {
  197.     ComponentWorkRequest req = (ComponentWorkRequest)(rootTable.get(root));
  198.     boolean newWorkRequest = (req == null);
  199.     if (newWorkRequest) {
  200.         req = new ComponentWorkRequest(root);
  201.     }
  202.  
  203.     /* At this point the ComponentWorkRequest may be accessible from
  204.      * an event dispatching thread so before updating it further
  205.      * we synchronize access to it.
  206.      */
  207.     synchronized(req) {
  208.         if (newWorkRequest) {
  209.         rootTable.put(root, req);
  210.         }
  211.         if (!req.isPending) {
  212.         SwingUtilities.invokeLater(req);
  213.         req.isPending = true;
  214.         }
  215.     }
  216.     }
  217.  
  218.  
  219.     /**
  220.      * Associate a RunnableCanvas and a JRootPane to enable queuing
  221.      * events for the root pane's parent window's event dispatching thread.
  222.      * Adds a 1x1 RunnableCanvas to the root pane's layered pane.
  223.      * <p>
  224.      * Called by JRootPane.addNotify() to set up the RunnableCanvas.
  225.      *
  226.      * @see RunnableCanvas
  227.      * @see JRootPane#addNotify
  228.      */
  229.     static synchronized void addRunnableCanvas(JRootPane rootPane)
  230.     {
  231.     /* If we have access to the system event queue, we don't bother
  232.      * with a RunnableCanvas
  233.      */
  234.     if (SystemEventQueue.get(rootPane) != null) {
  235.         return;
  236.     }
  237.  
  238.     JLayeredPane layeredPane = rootPane.getLayeredPane();
  239.     if (layeredPane != null) {
  240.         RunnableCanvas rc = new RunnableCanvas(rootPane);
  241.         layeredPane.add(rc);
  242.     }
  243.     }
  244.  
  245.  
  246.     /**
  247.      * Remove the RunnableCanvas from the JRootPane and clear the
  248.      * internal bookeeping associated with it.
  249.      * <p>
  250.      * Called by JRootPane.removeNotify()
  251.      *
  252.      * @see RunnableCanvas
  253.      */
  254.     static synchronized void removeRunnableCanvas(JRootPane rootPane) {
  255.     rootTable.remove(SwingUtilities.getRoot(rootPane));
  256.     RunnableCanvas.remove(rootPane);
  257.     }
  258.  
  259.  
  260.     /**
  261.      * Post an event to the AWT System event queue that, when dispatched,
  262.      * will invoke the specified Runnable.  If lock is non-null this call
  263.      * blocks (by waiting on the lock) until the doRun() method returns,
  264.      * otherwise we return as soon as the event has been enqueued.  An
  265.      * exception is only returned if lock is non-null, i.e. if we're
  266.      * being called from invokeAndWait().
  267.      * <p>
  268.      * This method is only intended to support SwingUtilities.invokeLater()
  269.      * and SwingUtilities.invokeAndWait().
  270.      */
  271.     static Exception postRunnable(Runnable doRun, Object lock)
  272.     {
  273.     EventQueue systemEventQueue = SystemEventQueue.get();
  274.  
  275.     RunnableEvent event = new RunnableEvent(doRun, lock);
  276.     if (systemEventQueue != null) {
  277.         systemEventQueue.postEvent(event);
  278.     }
  279.     else {
  280.         postRunnableCanvasEvent(event);
  281.     }
  282.     return event.exception;
  283.     }
  284.  
  285.  
  286.     /**
  287.      * Adds a RunnableEvent to all the remaining RunnableCanvases to restart
  288.      * the TimerQueues thread.
  289.      *
  290.      * @see RunnableCanvas#postRunnableEventToAll
  291.      */
  292.     static synchronized void restartTimerQueueThread() {
  293.     if (SystemEventQueue.get() == null) {
  294.         Runnable restarter = new TimerQueueRestart();
  295.         RunnableEvent event = new RunnableEvent(restarter, null);
  296.         RunnableCanvas.postRunnableEventToAll(event);
  297.     }
  298.     }
  299.  
  300.  
  301.     /**
  302.      * Runnable that will message the shared instance of the Timer Queue
  303.      * to restart.
  304.      *
  305.      * @see #restartTimerQueueThread
  306.      */
  307.     private static class TimerQueueRestart implements Runnable {
  308.     boolean attemptedStart;
  309.  
  310.     public synchronized void run() {
  311.         // Only try and restart the q once.
  312.         if(!attemptedStart) {
  313.         TimerQueue q = TimerQueue.sharedInstance();
  314.  
  315.         synchronized(q) {
  316.             if(!q.running)
  317.             q.start();
  318.         }
  319.         attemptedStart = true;
  320.         }
  321.     }
  322.     }
  323.  
  324.     /**
  325.      * Event type used for dispatching runnable objects for
  326.      * SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait().
  327.      *
  328.      * @see #postRunnable
  329.      */
  330.     private static class RunnableEvent extends AWTEvent {
  331.         static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 1000;
  332.     static final Component target = new RunnableTarget();
  333.     final Runnable doRun;
  334.     final Object lock;
  335.     Exception exception;
  336.  
  337.         RunnableEvent(Runnable doRun, Object lock) {
  338.             super(target, EVENT_ID);
  339.         this.doRun = doRun;
  340.         this.lock = lock;
  341.         }
  342.     }
  343.  
  344.  
  345.     /**
  346.      * Calls RunnableEvent.doRun.run().  If RunnableEvent.lock is non
  347.      * null then we synchronize the run() call and save the exception
  348.      * (if any) in the RunnableEvent.exception field.
  349.      */
  350.     private static void processRunnableEvent(RunnableEvent runnableEvent)
  351.     {
  352.     Object lock = runnableEvent.lock;
  353.     if (lock == null) {
  354.         runnableEvent.doRun.run();
  355.     }
  356.     else {
  357.         synchronized(lock) {
  358.         try {
  359.             runnableEvent.doRun.run();
  360.         }
  361.         catch (Exception e) {
  362.             runnableEvent.exception = e;
  363.         }
  364.         finally {
  365.             if (runnableEvent.lock != null) {
  366.             runnableEvent.lock.notify();
  367.             }
  368.         }
  369.         }
  370.     }
  371.     }
  372.  
  373.  
  374.     /**
  375.      * A dummy Component subclass that (only) handles RunnableEvents.  If the
  376.      * AWT System event queue is accessible (i.e. we're running as
  377.      * an application or as trusted code), RunnableEvents are dispatched
  378.      * to this component.
  379.      *
  380.      * @see #processRunnableEvent
  381.      */
  382.     private static class RunnableTarget extends Component
  383.     {
  384.         RunnableTarget() {
  385.             super();
  386.             enableEvents(RunnableEvent.EVENT_ID);
  387.         }
  388.  
  389.         protected void processEvent(AWTEvent event) {
  390.         if (event instanceof RunnableEvent) {
  391.         processRunnableEvent((RunnableEvent)event);
  392.         }
  393.         }
  394.     }
  395.  
  396.  
  397.     /**
  398.      * Synchronized entry point to the applet support for AWT System
  399.      * event queue access.  This method adds the event to the appropriate
  400.      * runnable canvas's queue and then has the canvas repaint().  Note
  401.      * that by the time the event dispatching thread gets to handling
  402.      * the repaint() (by calling runnableCanvas.update()), many runnable
  403.      * events may have been queued up.
  404.      *
  405.      * @see RunnableCanvas#addRunnableEvent
  406.      * @see RunnableCanvas#update
  407.      */
  408.     private static synchronized void postRunnableCanvasEvent(RunnableEvent e) {
  409.     RunnableCanvas runnableCanvas = RunnableCanvas.lookup(e);
  410.  
  411.     if (runnableCanvas == null) {
  412.  
  413.         /* If this is a ComponentWorkRequest and we were unable to
  414.          * queue it, then clear the pending flag.
  415.          */
  416.         if (e.doRun instanceof ComponentWorkRequest) {
  417.         ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
  418.             synchronized(req) {
  419.             req.isPending = false;
  420.         }
  421.         }
  422.  
  423.         /* If this is a Timer event let it know that it didn't fire.
  424.          */
  425.         if(e.doRun instanceof Timer.DoPostEvent) {
  426.         ((Timer.DoPostEvent)e.doRun).getTimer().eventQueued = false;
  427.         }
  428.  
  429.         /* We are unable to queue this event on a system event queue.  Make
  430.          * sure that any code that's waiting for the runnable to finish
  431.          * doesn't hang.
  432.          */
  433.         if (e.lock != null) {
  434.         e.lock.notify();
  435.         }
  436.         return;
  437.     }
  438.  
  439.     runnableCanvas.addRunnableEvent(e);
  440.     runnableCanvas.repaint();
  441.     }
  442.  
  443.     
  444.     /**
  445.      * Return the current threads ThreadGroup, even on IE4.0.
  446.      * IE4.0 throws a SecurityException if you apply getThreadGroup()
  447.      * to the event dispatching thread.  However a child of the
  448.      * event dispatching thread (same thread group) is OK.  
  449.      */
  450.     private static ThreadGroup getThreadGroupSafely() {
  451.     return new Thread().getThreadGroup();
  452.     }
  453.  
  454.  
  455.     /**
  456.      * Applets don't have direct access to the AWT SystemEvent queue.  To
  457.      * work around this we call RunnableCanvas.repaint() on a per applet
  458.      * instance of this class.  The AWT deals with this by queuing a
  459.      * java.awt.PaintEvent for the event dispatching thread which
  460.      * is dispatched (Component.dispatchEvent()) the usual way.
  461.      * Component.dispatchEvent() handles PaintEvents by calling our update()
  462.      * method (on the event dispatching thread) which processes
  463.      * the RunnableEvents stashed in the runnableEvents vector.
  464.      */
  465.      private static class RunnableCanvas extends Canvas
  466.      {
  467.      private static final Graphics nullGraphics = new RunnableCanvasGraphics();
  468.      private static Hashtable runnableCanvasTable = new Hashtable(1);
  469.      private Vector runnableEvents = new Vector(2);
  470.      private boolean isRegistered = false;
  471.  
  472.      RunnableCanvas(JRootPane rootPane) {
  473.          super();
  474.          setBounds(0, 0, 1, 1);
  475.  
  476.          /* Remember the mapping from the current thread (and the current
  477.           * thread group) to this RunnableCanvas.  Note that if a mapping
  478.           * has already been defined, e.g. this rootPane belongs to an
  479.           * existing applet, then leave the table alone.  We're assuming that
  480.           * an applets addNotify method will always run before the addNotify
  481.           * method in any subsidiary windows the applet creates can run.
  482.           */
  483.          if (runnableCanvasTable.get(Thread.currentThread()) == null) {
  484.          try {
  485.              runnableCanvasTable.put(Thread.currentThread(), this);
  486.              runnableCanvasTable.put(getThreadGroupSafely(), this);
  487.              if (SwingUtilities.isEventDispatchThread()) {
  488.              isRegistered = true;
  489.              }
  490.          }
  491.          catch(Exception e) {
  492.              System.err.println("Can't register RunnableCanvas");
  493.              e.printStackTrace();
  494.          }
  495.          }
  496.          runnableCanvasTable.put(rootPane, this);
  497.          maybeRegisterEventDispatchThread();
  498.      }
  499.  
  500.  
  501.      /**
  502.       * If called on an event dispatching thread that we haven't seen
  503.       * before then make two hashtable entries in the runnableCanvasTable:
  504.       * <pre>
  505.       *   current thread => this RunnableCanvas
  506.       *   current thread group => this RunnableCanvas
  507.       * </pre>
  508.       * @see #lookup
  509.       */
  510.      private void maybeRegisterEventDispatchThread() {
  511.          /* Avoid the cost of a synchronized block (or method) in the
  512.           * common case, since this method is called each time paint is called.
  513.           */
  514.          if (!isRegistered) {
  515.          synchronized(this) {
  516.              if (!isRegistered && SwingUtilities.isEventDispatchThread()) {
  517.              Thread currentThread = Thread.currentThread();
  518.  
  519.              /* If this event dispatching thread is already mapped to
  520.               * a runnableCanvas then don't replace the mapping (which
  521.               * we expect to be generated by the applet).
  522.               */
  523.              if (runnableCanvasTable.get(currentThread) != null) {
  524.                  isRegistered = true;
  525.              }
  526.              else {
  527.                  runnableCanvasTable.put(currentThread, this);
  528.                  runnableCanvasTable.put(getThreadGroupSafely(), this);
  529.                  isRegistered = true;
  530.              }
  531.              }
  532.          }
  533.          }
  534.      }
  535.  
  536.  
  537.      /**
  538.       * If we're running on the event dispatching thread then lookup
  539.       * the canvas with the current thread itself, otherwise use the
  540.       * current threads thread group.  If there is no match for the 
  541.       * ThreadGroup, the first visible RunnableCanvas is returned.
  542.       */
  543.      static RunnableCanvas lookup(RunnableEvent e) 
  544.      {
  545.          /* If this is a ComponentWorkRequest, find the components
  546.           * JRootPane ancestor and use that as the index into the
  547.           * runnableCanvasTable.  This case occurs when any thread,
  548.           * other than the event dispatching thead, calls repaint
  549.           */
  550.          if (e.doRun instanceof ComponentWorkRequest) {
  551.          ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
  552.          synchronized(req) {
  553.              JRootPane rootPane = SwingUtilities.getRootPane(req.component);
  554.              if(rootPane != null) {
  555.              return (RunnableCanvas)(runnableCanvasTable.get(rootPane));
  556.              }
  557.              /* Failure.  There doesn't appear to be a RunnableCanvas to use
  558.               * so indicate that a new request will need to be queued, see
  559.               * RepaintManager.queueWorkRequest().
  560.               */
  561.              req.isPending = false;
  562.              return null;
  563.          }
  564.          }
  565.  
  566.          /* If the current thread is in the runnableCanvasTable
  567.           * (e.g. we're on the event dispatching thread) we're done.
  568.           */
  569.          Object rv = runnableCanvasTable.get(Thread.currentThread());
  570.          if (rv != null) {
  571.          return (RunnableCanvas)rv;
  572.          }
  573.  
  574.          /* At this point we're assuming that the calling thread isn't
  575.           * a system thread (like an image observer thread), so it's safe 
  576.           * to lookup via the current threads ThreadGroup.
  577.           */
  578.          Object threadGroup;
  579.          try {
  580.          threadGroup = Thread.currentThread().getThreadGroup();
  581.          }
  582.          catch (SecurityException exc) {
  583.          return null;
  584.          }
  585.          RunnableCanvas rc = (RunnableCanvas)runnableCanvasTable.get(threadGroup);
  586.          
  587.          /* There's no RunnableCanvas associated with this thread group
  588.           * (so punt).  Return the first visible RunnableCanvas.
  589.           */
  590.          if(rc == null) {
  591.          Enumeration keys = runnableCanvasTable.keys();
  592.          if(keys == null) {
  593.              return null;
  594.          }
  595.          while(keys.hasMoreElements()) {
  596.              Object key = keys.nextElement();
  597.              if ((key instanceof JRootPane) && ((JRootPane)key).isShowing()) {
  598.              return (RunnableCanvas)runnableCanvasTable.get(key);
  599.              }
  600.          }
  601.          }
  602.  
  603.          return rc;
  604.      }
  605.  
  606.  
  607.      /**
  608.       * Adds the event to all the RunnableCanvases.
  609.       *
  610.       * @see #restartTimerQueueThread
  611.       */
  612.      static void postRunnableEventToAll(RunnableEvent e) {
  613.          // Determine the RunnableCanvas for the current thread. It
  614.          // may be null.
  615.          RunnableCanvas currentThreadCanvas;
  616.          ThreadGroup tg;
  617.          try {
  618.          tg = new Thread().getThreadGroup();
  619.          }
  620.          catch (SecurityException se) {
  621.          tg = null;
  622.          }
  623.          if(tg != null) {
  624.          currentThreadCanvas = (RunnableCanvas)runnableCanvasTable.
  625.                                 get(tg);
  626.          }
  627.          else
  628.          currentThreadCanvas = null;
  629.  
  630.          // Add the event to all canvases, except the current one.
  631.          // Presumably the current one is no longer valid and will be
  632.          // going away shortly.
  633.          Enumeration keys = runnableCanvasTable.keys();
  634.          while(keys.hasMoreElements()) {
  635.          Object key = keys.nextElement();
  636.          if(key instanceof JRootPane) {
  637.              Object canvas = runnableCanvasTable.get(key);
  638.              if(canvas != currentThreadCanvas) {
  639.              RunnableCanvas rc = (RunnableCanvas)canvas;
  640.              rc.addRunnableEvent(e);
  641.              rc.repaint();
  642.              }
  643.          }
  644.          }
  645.      }
  646.  
  647.  
  648.      /**
  649.       * Remove the RunnableCanvas associated with this applet from the
  650.       * applets Layered pane and clear all of the runnableCanvasTable
  651.       * entries that point at it.
  652.       */
  653.      static void remove(JRootPane rootPane) {
  654.          RunnableCanvas rc = (RunnableCanvas)(runnableCanvasTable.get(rootPane));
  655.          if (rc != null) {
  656.          RunnableCanvas nextCanvas = null;
  657.          JLayeredPane layeredPane = rootPane.getLayeredPane();
  658.          layeredPane.remove((Component)rc);
  659.  
  660.          Enumeration keys = runnableCanvasTable.keys();
  661.          while(keys.hasMoreElements()) {
  662.              Object key = keys.nextElement();
  663.              Object next = runnableCanvasTable.get(key);
  664.              if (rc == next) {
  665.              runnableCanvasTable.remove(key);
  666.              }
  667.              else if(nextCanvas == null) {
  668.              nextCanvas = (RunnableCanvas)next;
  669.              }
  670.          }
  671.  
  672.          // If there are still events, either move them to another
  673.          // canvas, or mark the Timer type events as not having
  674.          // fired.
  675.          RunnableEvent[] events = rc.getRunnableCanvasEvents();
  676.          int numEvents = (events == null) ? 0 : events.length;
  677.          if(numEvents > 0) {
  678.              if(nextCanvas != null) {
  679.              for(int counter = 0; counter < numEvents; counter++) {
  680.                  RunnableEvent e = events[counter];
  681.                  if(e.doRun instanceof Timer.DoPostEvent)
  682.                  nextCanvas.addRunnableEvent(e);
  683.              }
  684.              nextCanvas.repaint();
  685.              }
  686.              else {
  687.              // Mark all Timer type event as not having fired.
  688.              for(int counter = 0; counter < numEvents; counter++) {
  689.                  RunnableEvent event = events[counter];
  690.                  if(event.doRun instanceof Timer.DoPostEvent) {
  691.                  ((Timer.DoPostEvent)event.doRun).getTimer().
  692.                                      eventQueued = false;
  693.                  }
  694.              }
  695.              }
  696.          }
  697.          }
  698.      }
  699.  
  700.  
  701.      /**
  702.       * If there are events to be processed then we're showing.  Note
  703.       * that the AWT code that dispatches paint events short circuits
  704.       * (does nothing) if isShowing() returns false.
  705.       */
  706.      public boolean isShowing() {
  707.          return runnableEvents.size() > 0;
  708.      }
  709.  
  710.  
  711.      /**
  712.       * Reduce the cost of repainting (since we're not going to draw
  713.       * anything) by returning a constant no-op graphics object.
  714.       */
  715.      public Graphics getGraphics() {
  716.          return nullGraphics;
  717.      }
  718.  
  719.  
  720.      /**
  721.       * Testing purposes only.  This method shouldn't be called;
  722.       * the parent of this component should have a null layout
  723.       * manager.
  724.       */
  725.      public Dimension getPreferredSize() {
  726.          return new Dimension(1, 1);
  727.      }
  728.  
  729.  
  730.      /**
  731.       * Add a RunnableEvent to the queue that will be dispatched
  732.       * when this component is repainted.
  733.       * @see #update
  734.       */
  735.      synchronized void addRunnableEvent(RunnableEvent e) {
  736.          runnableEvents.addElement(e);
  737.      }
  738.  
  739.  
  740.      /**
  741.       * Return an (array) copy of the runnableEvents vector or
  742.       * null if the vector is empty.  The update method processes
  743.       * a copy of the vector so that we don't have to hold
  744.       * the synchronized lock while calling processRunnableEvent().
  745.       * @see #update
  746.       */
  747.      private synchronized RunnableEvent[] getRunnableCanvasEvents() {
  748.          int n = runnableEvents.size();
  749.          if (n == 0) {
  750.          return null;
  751.          }
  752.          else {
  753.          RunnableEvent[] rv = new RunnableEvent[n];
  754.          for(int i = 0; i < n; i++) {
  755.              rv[i] = (RunnableEvent)(runnableEvents.elementAt(i));
  756.          }
  757.          runnableEvents.removeAllElements();
  758.          return rv;
  759.          }
  760.      }
  761.  
  762.  
  763.      public void paint(Graphics g) {
  764.          maybeRegisterEventDispatchThread();
  765.      }
  766.  
  767.  
  768.      /**
  769.       * Process all of the RunnableEvents that have accumulated
  770.       * since RunnableCanvas.repaint() was called.
  771.       */
  772.      public void update(Graphics g) {
  773.          RunnableEvent[] events = getRunnableCanvasEvents();
  774.          if (events != null) {
  775.          for(int i = 0; i < events.length; i++) {
  776.              processRunnableEvent(events[i]);
  777.          }
  778.          }
  779.      }
  780.     }
  781.  
  782.  
  783.     /**
  784.      * A no-op Graphics object for the RunnableCanvas component.
  785.      * Most AWT Component implementations handle update events
  786.      * like this:
  787.      * <pre>
  788.      *      Graphics g = getGraphics();
  789.      *      Rectangle r = ((PaintEvent)e).getUpdateRect();
  790.      *      g.clipRect(r.x, r.y, r.width, r.height);
  791.      *      update(g)
  792.      *      g.dispose();
  793.      * </pre>
  794.      * Since the RunnableCanvas component isn't really going to do
  795.      * any painting we don't bother with creating/disposing real
  796.      * graphics objects, or setting any of the properties.
  797.      *
  798.      */
  799.     private static class RunnableCanvasGraphics extends Graphics
  800.     {
  801.     public Graphics create() {
  802.         return this;
  803.     }
  804.  
  805.     /* We don't expect any of the following methods to be called but
  806.      * we still return marginally valid values for the get methods
  807.      * just in case.
  808.      */
  809.  
  810.     public Rectangle getClipBounds() {
  811.         return new Rectangle(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
  812.     }
  813.  
  814.     public Shape getClip() {
  815.         return (Shape)(getClipBounds());
  816.     }
  817.  
  818.     public  void dispose() {}
  819.     public void translate(int x, int y) {}
  820.     public Color getColor() { return Color.black; }
  821.     public void setColor(Color c) {}
  822.     public void setPaintMode() {}
  823.     public void setXORMode(Color c) {}
  824.     public Font getFont() { return null; }
  825.     public void setFont(Font font) {}
  826.     public FontMetrics getFontMetrics(Font f) { return null; }
  827.     public void clipRect(int x, int y, int width, int height) {}
  828.     public void setClip(int x, int y, int width, int height) {}
  829.     public void setClip(Shape clip) {}
  830.     public void copyArea(int x, int y, int w, int h, int dx, int dy) {}
  831.     public void drawLine(int x1, int y1, int x2, int y2) {}
  832.     public void fillRect(int x, int y, int width, int height) {}
  833.     public void clearRect(int x, int y, int width, int height) {}
  834.     public void drawRoundRect(int x, int y, int w, int h, int aw, int ah) {}
  835.     public void fillRoundRect(int x, int y, int w, int h, int aw, int ah) {}
  836.     public void drawOval(int x, int y, int w, int h) {}
  837.     public void fillOval(int x, int y, int w, int h) {}
  838.     public void drawArc(int x, int y, int w, int h, int sa, int aa) {}
  839.     public void fillArc(int x, int y, int w, int h, int sa, int aa) {}
  840.     public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {}
  841.     public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {}
  842.     public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {}
  843.     public void drawString(String str, int x, int y) {}
  844.         
  845.         public void drawString(java.text.AttributedCharacterIterator iterator, int x, int y) {}
  846.          
  847.     public boolean drawImage(Image i, int x, int y, ImageObserver o) { return false; }
  848.     public boolean drawImage(Image i, int x, int y, int w, int h, ImageObserver o) { return false; }
  849.     public boolean drawImage(Image i, int x, int y, Color bgcolor, ImageObserver o) { return false; }
  850.     public boolean drawImage(Image i, int x, int y, int w, int h, Color c, ImageObserver o) { return false; }
  851.     public boolean drawImage(Image i,
  852.             int dx1, int dy1, int dx2, int dy2,
  853.         int sx1, int sy1, int sx2, int sy2, ImageObserver o)
  854.         { return false; }
  855.     public boolean drawImage(Image i,
  856.             int dx1, int dy1, int dx2, int dy2,
  857.         int sx1, int sy1, int sx2, int sy2, Color c, ImageObserver o)
  858.         { return false; }
  859.     }
  860.  
  861.  
  862.     /* Print a warning if we're running the wrong version of this class for
  863.      * this JDK.
  864.      */
  865.     static {
  866.         
  867.           if (!SwingUtilities.is1dot2) {
  868.               System.err.println("warning: running 1.2 version of SystemEventQueueUtilities");
  869.           }
  870.           
  871.  
  872.  
  873.  
  874.  
  875.  
  876.  
  877.  
  878.  
  879.     }
  880. }
  881.